package com.pisolution.scheduler.runtime.worker;

import com.pisolution.scheduler.core.utils.Stopper;
import com.pisolution.scheduler.remote.NettyRemoteServer;
import com.pisolution.scheduler.remote.command.CommandType;
import com.pisolution.scheduler.runtime.worker.processor.DBAckProcessor;
import com.pisolution.scheduler.runtime.worker.processor.TaskExecuteProcessor;
import com.pisolution.scheduler.runtime.worker.processor.TaskKillProcessor;
import com.pisolution.scheduler.runtime.worker.reporter.TaskStateReporter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.ComponentScan;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

/**
 * 调度平台工作节点.
 *
 * @author sora
 * @version 0.1.0
 */
@ComponentScan(value = "com.pisolution.scheduler")
@EnableAutoConfiguration
// TODO: 移除注解 @EnableAutoConfiguration
public final class WorkerServer {
    private static final Logger LOGGER = LoggerFactory.getLogger(WorkerServer.class);

    // -------------------------------------------------------------------------------------------------

    /** 用于配置工作节点. */
    @Resource
    private WorkerConfig config;

    /** 用于开放服务以便于节点组件之间的信息交互. */
    private NettyRemoteServer server;

    /** 用于注册管理节点. */
    @Resource
    private WorkerRegistry registry;

    /** 用于发送缓存中的确认指令与响应指令给管理节点. */
    @Resource
    private TaskStateReporter reporter;

    // -------------------------------------------------------------------------------------------------
    //  生命周期
    // -------------------------------------------------------------------------------------------------

    /**
     * 启动工作节点上的应用服务.
     *
     * <ol>
     *     <li>启动远程通讯服务 用于响应调度系统内其他组件的通讯请求</li>
     *     <li>在注册中心注册工作节点并定期上传自身心跳信息</li>
     *     <li>启动可重试任务状态汇报器 用于发送缓存中的确认指令与响应指令给管理节点</li>
     * </ol>
     *
     * <p><b>注意:</b></p>
     * <p>该方法会在构造函数与依赖注入之后自动调用 无需手动触发.</p>
     */
    @SuppressWarnings("unused")
    @PostConstruct
    public void start() {
        LOGGER.info("Start the worker server.");

        // 1. 初始化交互服务.
        this.server = new NettyRemoteServer(this.config.getPort());

        // 2. 注册请求处理器并启动交互服务.
        this.server
            .register(CommandType.TASK_EXECUTE_REQUEST, new TaskExecuteProcessor())
            .register(CommandType.TASK_KILL_REQUEST, new TaskKillProcessor())
            .register(CommandType.DB_ACK_REQUEST, new DBAckProcessor())
            .start();

        // 3. 注册工作节点.
        this.registry.register();

        // 4. 启动可重试任务状态汇报器.
        this.reporter.start();

        // 5. 如果工作节点关闭则清理启动环境.
        Runtime.getRuntime().addShutdownHook(new Thread(this::close));
    }

    /**
     * 清理工作节点启动时启动的服务 待清理的服务列表可参考启动时的描述.
     */
    private void close() {
        try {
            if (Stopper.isStopped()) {
                return;
            }

            LOGGER.info("Shut down the worker server.");

            Stopper.stop();

            // 1. 休眠一段时间以便所有线程退出.
            try {
                Thread.sleep(3000L);
            } catch (Exception ignored) {
                // 无需处理此异常.
            }

            // 2. 关闭工作节点服务 (顺序敏感).
            this.server.stop();
            this.registry.unregister();
        } catch (Exception e) {
            LOGGER.error("Could not shut down the worker server.", e);

            System.exit(1);
        }
    }

    // -------------------------------------------------------------------------------------------------
    //  启动入口
    // -------------------------------------------------------------------------------------------------

    /**
     * 启动工作节点并使用给定的额外参数配置工作节点.
     *
     * @param args 用于额外配置工作节点的参数.
     */
    public static void main(String[] args) {
        new SpringApplicationBuilder(WorkerServer.class)
            .web(WebApplicationType.NONE)
            .run(args);
    }
}
